﻿using System.Linq;
using Xenta.Entities;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Represents the address create operation base.
    /// </summary>
    /// <typeparam name="TAddr">The type of address entity.</typeparam>
    public class CreateAddress<TAddr> : CreateAuditable<TAddr>
        where TAddr : AddressEntity
    {
        #region Properties

        /// <summary>
        /// Gets or sets the holder identifier.
        /// </summary>
        public int HolderID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the country identifier.
        /// </summary>
        public int CountryID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region identifier.
        /// </summary>
        public int RegionID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the city.
        /// </summary>
        public string City
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 1.
        /// </summary>
        public string Address1
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 2.
        /// </summary>
        public string Address2
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the entity name.
        /// </summary>
        public string EntityName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the contact name.
        /// </summary>
        public string ContactName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the postal code.
        /// </summary>
        public string PostalCode
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the phone number.
        /// </summary>
        public string PhoneNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the fax number.
        /// </summary>
        public string FaxNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the website.
        /// </summary>
        public string Website
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result.Country.EntityID != CountryID)
                return Err.Of<RegionEntity>().NotIn("Country");
            return base.Validate(uow);
        }

        protected override void Construct(IUnitOfWork uow, TAddr entity)
        {
            base.Construct(uow, entity);

            entity.HolderID = HolderID;
            entity.Country = uow.Execute(new GetCountry
            {
                ThrowsException = true,
                EntityID = CountryID
            }).Result;
            entity.Region = uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result;
            entity.City = StringHelper.EnsureNotNull(City).TrimToLen(100);
            entity.Address1 = StringHelper.EnsureNotNull(Address1).TrimToLen(100);
            entity.Address2 = StringHelper.EnsureNotNull(Address2).TrimToLen(100);
            entity.EntityName = StringHelper.EnsureNotNull(EntityName).TrimToLen(100);
            entity.ContactName = StringHelper.EnsureNotNull(ContactName).TrimToLen(100);
            entity.PostalCode = StringHelper.EnsureNotNull(PostalCode).TrimToLen(100);
            entity.PhoneNumber = StringHelper.EnsureNotNull(PhoneNumber).TrimToLen(100);
            entity.FaxNumber = StringHelper.EnsureNotNull(FaxNumber).TrimToLen(100);
            entity.Email = StringHelper.EnsureNotNull(Email).TrimToLen(100);
            entity.Website = StringHelper.EnsureNotNull(Website).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Represents the address update operation base.
    /// </summary>
    /// <typeparam name="TAddr">The type of address entity.</typeparam>
    public abstract class UpdateAddress<TAddr> : UpdateAuditable<TAddr>
        where TAddr : AddressEntity
    {
        #region Properties

        /// <summary>
        /// Gets or sets the country identifier.
        /// </summary>
        public int CountryID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region identifier.
        /// </summary>
        public int RegionID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the city.
        /// </summary>
        public string City
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 1.
        /// </summary>
        public string Address1
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 2.
        /// </summary>
        public string Address2
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the entity name.
        /// </summary>
        public string EntityName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the contact name.
        /// </summary>
        public string ContactName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the postal code.
        /// </summary>
        public string PostalCode
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the phone number.
        /// </summary>
        public string PhoneNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the fax number.
        /// </summary>
        public string FaxNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the website.
        /// </summary>
        public string Website
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result.Country.EntityID != CountryID)
                return Err.Of<RegionEntity>().NotIn("Country");
            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, TAddr entity)
        {
            base.Revise(uow, entity);

            entity.Country = uow.Execute(new GetCountry
            {
                ThrowsException = true,
                EntityID = CountryID
            }).Result;
            entity.Region = uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result;
            entity.City = StringHelper.EnsureNotNull(City).TrimToLen(100);
            entity.Address1 = StringHelper.EnsureNotNull(Address1).TrimToLen(100);
            entity.Address2 = StringHelper.EnsureNotNull(Address2).TrimToLen(100);
            entity.EntityName = StringHelper.EnsureNotNull(EntityName).TrimToLen(100);
            entity.ContactName = StringHelper.EnsureNotNull(ContactName).TrimToLen(100);
            entity.PostalCode = StringHelper.EnsureNotNull(PostalCode).TrimToLen(100);
            entity.PhoneNumber = StringHelper.EnsureNotNull(PhoneNumber).TrimToLen(100);
            entity.FaxNumber = StringHelper.EnsureNotNull(FaxNumber).TrimToLen(100);
            entity.Email = StringHelper.EnsureNotNull(Email).TrimToLen(100);
            entity.Website = StringHelper.EnsureNotNull(Website).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Deletes the address.
    /// </summary>
    /// <typeparam name="TAddr">The type of address entity.</typeparam>
    public class DeleteAddress<TAddr> : DeleteEntity<TAddr>
        where TAddr : AddressEntity
    {
    }

    /// <summary>
    /// Gets the address by identifier.
    /// </summary>
    /// <typeparam name="TAddr">The type of address entity.</typeparam>
    public class GetAddress<TAddr> : GetEntity<TAddr>
        where TAddr : AddressEntity
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Represents the address search operation base.
    /// </summary>
    public abstract class SearchAddresses<TAddr> : SearchOperation<TAddr>
        where TAddr : AddressEntity
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        protected SearchAddresses()
        {
            Term = SearchTerm.Empty;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the country identifier.
        /// </summary>
        public int? CountryID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region identifier.
        /// </summary>
        public int? RegionID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the holder identifier.
        /// </summary>
        public int? HolderID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<TAddr> Build(IUnitOfWork uow, IQueryable<TAddr> query)
        {
            query = base.Build(uow, query);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x => x.City.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x => x.City.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x => x.City.Contains(Term.Keyword) ||
                        x.EntityName.Contains(Term.Keyword) ||
                        x.ContactName.Contains(Term.Keyword) ||
                        x.PostalCode.Contains(Term.Keyword) ||
                        x.PhoneNumber.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x => x.City.Equals(Term.Keyword));
                    break;
            }
            if(CountryID.HasValue)
                query = query.Where(x => x.Country.EntityID == CountryID.Value);
            if(RegionID.HasValue)
                query = query.Where(x => x.Region.EntityID == RegionID.Value);
            if(HolderID.HasValue)
                query = query.Where(x => x.HolderID == HolderID.Value);
            return query;
        }

        #endregion
    }

    #endregion
}
